package cn.dansj.utils.interfaces;

import cn.dansj.common.jdbc.DBUtils;
import cn.dansj.common.context.ApplicationContextProvider;
import cn.dansj.common.utils.transfer.StringUtils;
import cn.dansj.common.utils.transfer.Transformation;
import oracle.jdbc.OracleTypes;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CallStatement {
    private final static Pattern pattern_1 = Pattern.compile("(\\$\\{)(.*?)(})");
    private final static Pattern pattern_2 = Pattern.compile("(#\\{)(.*?)(})");
    private final static JdbcTemplate jdbcTemplate = ApplicationContextProvider.getBean(JdbcTemplate.class);

    public static ParseOutput parseMybatisParams(String callPrcSql, Map<String, Object> paramsMap) {
        if (callPrcSql.endsWith(";")) {
            callPrcSql = callPrcSql.substring(0, callPrcSql.length() - 1);
        }

        Pattern compile = Pattern.compile("[$#]\\{.*?}");
        Matcher matcher = compile.matcher(callPrcSql);
        Map<String, Map<String, String>> inParamsType = new HashMap<>();
        Map<Integer, Map<String, String>> outParamsType = new HashMap<>();
        int index = 1;
        while (matcher.find()) {
            String group = matcher.group();
            String name = StringUtils.split(group.substring(2, group.length() - 1), ",")[0];
            String jdbcType = Transformation.nvl(Transformation.patternGet(group, "(?<=jdbcType=)[\\s\\S]*?(?=[,}])", null), "VARCHAR");
            String mode = getMode(group);
            Map<String, String> param = new HashMap<>();
            param.put("mode", mode);
            param.put("jdbcType", jdbcType);
            param.put("name", name);
            if ("in".equalsIgnoreCase(mode)) {
                callPrcSql = callPrcSql.replace(group, group.charAt(0) + "{" + name + "}");
                inParamsType.put(name, param);
            } else {
                callPrcSql = callPrcSql.replace(group, "?");
                outParamsType.put(index, param);
                index++;
            }
        }

        Matcher matcher_1 = pattern_1.matcher(callPrcSql);
        while (matcher_1.find()) {
            //在map中的显示,map中不带 ${}
            String group = matcher_1.group(2);

            //在text中的显示,text原始格式带${}
            String inTextGroup = matcher_1.group(0);
            if (getMode(inTextGroup).equals("OUT")) {
                continue;
            }
            String replace = paramsMap.get(group).toString();
            if (replace != null) {
                callPrcSql = StringUtils.replace(callPrcSql, inTextGroup, replace);
            }
        }

        Matcher matcher_2 = pattern_2.matcher(callPrcSql);
        while (matcher_2.find()) {
            //在map中的显示,map中不带 ${}
            String group = matcher_2.group(2);
            //在text中的显示,text原始格式带${}
            String inTextGroup = matcher_2.group(0);

            if (getMode(inTextGroup).equals("OUT")) {
                continue;
            }

            String replace = paramsMap.get(group).toString();
            if (replace != null) {
                if ("varchar".equalsIgnoreCase(inParamsType.get(group).get("jdbcType"))) {
                    callPrcSql = StringUtils.replace(callPrcSql, inTextGroup, "'" + replace + "'");
                } else {
                    callPrcSql = StringUtils.replace(callPrcSql, inTextGroup, replace);
                }
            }
        }
        return new ParseOutput(callPrcSql, outParamsType);
    }

    public static Map<String, List<Map<String, Object>>> exec(ParseOutput parseOutput) {

        Map<Integer, Map<String, String>> outParamsType = parseOutput.outParams;
        String storedProc = parseOutput.sql;

        List<String> unparsed = Transformation.checkUnParsedVariable(storedProc);
        if (unparsed.size() > 0) {
            throw new IllegalArgumentException("存在未解析参数:" + String.join(",", unparsed));
        }

        return jdbcTemplate.execute(
                con -> {
                    CallableStatement cs = con.prepareCall(storedProc);
                    for (int outIndex : outParamsType.keySet()) {
                        cs.registerOutParameter(outIndex, getType(outParamsType.get(outIndex).get("jdbcType")));// 注册输出参数的类型 OracleTypes.CURSOR
                    }
                    return cs;
                }, (CallableStatementCallback<Map<String, List<Map<String, Object>>>>) cs -> {
                    cs.execute();
                    Map<String, List<Map<String, Object>>> output = new HashMap<>();
                    for (int outIndex : outParamsType.keySet()) {
                        ResultSet rs = (ResultSet) cs.getObject(outIndex);// 获取游标一行的值
                        List<Map<String, Object>> resultsMap = DBUtils.resultSetToJson(rs);
                        output.put(outParamsType.get(outIndex).get("name"), resultsMap);
                        rs.close();
                    }
                    return output;
                });
    }

    public static String getMode(String v) {
        return Transformation.nvl(Transformation.patternGet(v.replaceAll("\\s", ""), "(?<=mode=)[\\s\\S]*?(?=[,} ])", null), "IN").toUpperCase();
    }

    public static int getType(String name) {
        try {
            Field field = OracleTypes.class.getDeclaredField(name.toUpperCase());
            field.setAccessible(true);
            return (int) field.get(OracleTypes.class);
        } catch (Exception exception) {
            throw new IllegalArgumentException("jdbcType:" + name + " 不存在");
        }
    }

    public record ParseOutput(String sql, Map<Integer, Map<String, String>> outParams) {
    }
}
